<script setup lang="ts">
import {
  Cartesian3,
  Matrix3,
  Math as CesiumMath,
  Terrain,
  Viewer,
  Transforms,
  HeadingPitchRoll,
  HeadingPitchRange,
  Model,
  Ellipsoid,
  ModelAnimationLoop,
  Matrix4,
  JulianDate,
  SampledPositionProperty,
  PolylineGlowMaterialProperty,
  Color,
  HeightReference,
} from "cesium";
import { onMounted } from "vue";

onMounted(() => {
  init();
});

const init = async () => {
  const viewer = new Viewer("cesiumContainer", {
    // requestRenderMode: true,
    shouldAnimate: true,
    terrain: Terrain.fromWorldTerrain(),
    infoBox: false,
    animation: false,
    timeline: false,
    navigationHelpButton: false,
    sceneModePicker: false,
    baseLayerPicker: true,
    fullscreenButton: false,
    geocoder: false,
    homeButton: false,
    scene3DOnly: true,
  });

  (viewer.cesiumWidget.creditContainer as HTMLElement).style.display = "none";

  const scene = viewer.scene;
  const camera = scene.camera;

  const pathPosition = new SampledPositionProperty();
  viewer.entities.add({
    position: pathPosition,
    name: "path",
    path: {
      show: true,
      leadTime: 0,
      trailTime: 60,
      width: 10,
      resolution: 1,
      material: new PolylineGlowMaterialProperty({
        glowPower: 0.3,
        taperPower: 0.3,
        color: Color.PALEGOLDENROD,
      }),
    },
  });

  const controller = scene.screenSpaceCameraController;
  let r = 0;

  const hpRoll = new HeadingPitchRoll();
  const hpRange = new HeadingPitchRange();
  let speed = 1;
  const deltaRadians = CesiumMath.toRadians(3.0);

  let position = Cartesian3.fromDegrees(-123.0744619, 44.0503706, 5000.0);
  let speedVector = new Cartesian3();

  const fixedFrameTransform = Transforms.localFrameToFixedFrameGenerator(
    "north",
    "west"
  );

  try {
    const planePrimitive = scene.primitives.add(
      await Model.fromGltfAsync({
        // url: "/static/models/CesiumAir.glb",
        url: "/static/models/character-male-e.glb",
        modelMatrix: Transforms.headingPitchRollToFixedFrame(
          position,
          hpRoll,
          Ellipsoid.WGS84,
          fixedFrameTransform
        ),
        minimumPixelSize: 16 * 8,
        debugShowBoundingVolume: false,
        heightReference: HeightReference.CLAMP_TO_GROUND,
        scene: scene,
      })
    );

    // console.log("planePrimitive", planePrimitive);

    planePrimitive.readyEvent.addEventListener(() => {
      // Play and loop all animations at half-speed
      planePrimitive.activeAnimations.add({
        index: 2,
        multiplier: 0.5,
        loop: ModelAnimationLoop.REPEAT,
      });

      // Zoom to model
      const max = Math.max(
        planePrimitive.boundingSphere.radius,
        camera.frustum.near
      );
      r = 2.0 * max;
      controller.minimumZoomDistance = r * 1;
      // controller.minimumZoomDistance = 0.1;
      const center = planePrimitive.boundingSphere.center;
      const heading = CesiumMath.toRadians(230.0);
      const pitch = CesiumMath.toRadians(-20.0);
      hpRange.heading = heading;
      hpRange.pitch = pitch;
      hpRange.range = r * 10;
      camera.lookAt(center, hpRange);
    });

    document.addEventListener("keydown", function (e) {
      // console.log("keydown", e.code);

      switch (e.code) {
        case "ArrowDown":
          if (e.shiftKey) {
            // speed down
            speed = Math.max(--speed, 1);
          } else {
            // pitch down
            hpRoll.pitch -= deltaRadians;
            if (hpRoll.pitch < -CesiumMath.TWO_PI) {
              hpRoll.pitch += CesiumMath.TWO_PI;
            }
          }
          break;
        case "ArrowUp":
          if (e.shiftKey) {
            // speed up
            speed = Math.min(++speed, 100);
          } else {
            // pitch up
            hpRoll.pitch += deltaRadians;
            if (hpRoll.pitch > CesiumMath.TWO_PI) {
              hpRoll.pitch -= CesiumMath.TWO_PI;
            }
          }
          break;
        case "ArrowRight":
          if (e.shiftKey) {
            // roll right
            hpRoll.roll += deltaRadians;
            if (hpRoll.roll > CesiumMath.TWO_PI) {
              hpRoll.roll -= CesiumMath.TWO_PI;
            }
          } else {
            // turn right
            hpRoll.heading += deltaRadians;
            if (hpRoll.heading > CesiumMath.TWO_PI) {
              hpRoll.heading -= CesiumMath.TWO_PI;
            }
          }
          break;
        case "ArrowLeft":
          if (e.shiftKey) {
            // roll left until
            hpRoll.roll -= deltaRadians;
            if (hpRoll.roll < 0.0) {
              hpRoll.roll += CesiumMath.TWO_PI;
            }
          } else {
            // turn left
            hpRoll.heading -= deltaRadians;
            if (hpRoll.heading < 0.0) {
              hpRoll.heading += CesiumMath.TWO_PI;
            }
          }
          break;
        case "KeyW":
          forward();
          break;
        case "Space":
          changeSight();
          break;
        default:
      }

      // scene.requestRender();
    });

    const forward = () => {
      speedVector = Cartesian3.multiplyByScalar(
        Cartesian3.UNIT_X,
        speed / 2,
        speedVector
      );
      position = Matrix4.multiplyByPoint(
        planePrimitive.modelMatrix,
        speedVector,
        position
      );
      pathPosition.addSample(JulianDate.now(), position);
      Transforms.headingPitchRollToFixedFrame(
        position,
        hpRoll,
        Ellipsoid.WGS84,
        fixedFrameTransform,
        planePrimitive.modelMatrix
      );

      const center = planePrimitive.boundingSphere.center;
      hpRange.heading = hpRoll.heading;
      hpRange.pitch = hpRoll.pitch;
      camera.lookAt(center, hpRange);
    };

    let isSpace = false;

    const changeSight = () => {
      isSpace = !isSpace;
    };

    viewer.scene.preUpdate.addEventListener(function () {
      speedVector = Cartesian3.multiplyByScalar(
        Cartesian3.UNIT_X,
        speed / 10,
        speedVector
      );
      position = Matrix4.multiplyByPoint(
        planePrimitive.modelMatrix,
        speedVector,
        position
      );

      pathPosition.addSample(JulianDate.now(), position);

      Transforms.headingPitchRollToFixedFrame(
        position,
        hpRoll,
        Ellipsoid.WGS84,
        fixedFrameTransform,
        planePrimitive.modelMatrix
      );

      // console.log("hpRoll", hpRoll);

      setTimeout(() => {
        const { center } = planePrimitive.boundingSphere;
        // center.y -= radius * 0;
        // center.x -= radius * 16;
        // center.z -= radius * 16;
        // hpRange.pitch = hpRoll.pitch;

        if (isSpace) {
          hpRange.heading = hpRoll.heading;

          // camera.lookAtTransform(Matrix4.IDENTITY);

          const orientation = planePrimitive.orientation;

          // 以车辆建立局部坐标系
          let transform = Transforms.eastNorthUpToFixedFrame(center);
          // 取车辆的朝向，让车辆指向正北方向
          transform = Matrix4.fromRotationTranslation(
            Matrix3.fromQuaternion(orientation),
            center
          );

          // lookAtTransform的第二个参数，设置相机的朝向，heading设置为90则相机视角跟在车辆后面
          viewer.camera.lookAtTransform(
            transform,
            new HeadingPitchRange(
              CesiumMath.toRadians(90),
              CesiumMath.toRadians(-10.0),
              25
            )
          );

          // camera.lookAtTransform(center, hpRange);

          // camera.setView({
          //   destination: center,
          //   orientation: hpRange,
          // });
        } else {
          hpRange.heading = hpRoll.heading; // 镜头前后
          hpRange.pitch = hpRoll.pitch - 0.05 * Math.PI; // 镜头上下
          hpRange.range = r * 10; // 镜头远近
          camera.lookAt(center, hpRange);
        }
      }, 1000);
    });
  } catch (error) {
    console.log(`Error loading model: ${error}`);
  }
};
</script>

<template>
  <div id="cesiumContainer" class="czm-ctnr"></div>
</template>

<style lang="scss" scoped>
.czm-ctnr {
  width: 100%;
  height: 100%;
}
</style>
